﻿using Furion.DataEncryption;
using System.Reflection;
using System.Security.Claims;

namespace GoodAdmin.Web.Core;

public class JwtHandler : AppAuthorizeHandler
{
    /// <summary>
    /// 自动刷新Token
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override async Task HandleAsync(AuthorizationHandlerContext context)
    {
        var expire = App.GetOptions<JWTSettingsOptions>().ExpiredTime.ToInt();//获取过期时间(分钟)
        var currentHttpContext = context.GetCurrentHttpContext();
        if (JWTEncryption.AutoRefreshToken(context, currentHttpContext,
                expire, expire * 2))
        {
            //验证token是否有效
            if (CheckToken(currentHttpContext, expire))
            {
                await AuthorizeHandleAsync(context);
            }
            else
            {
                currentHttpContext.Response.StatusCode = 401;
                return;
            }  
        }
        else
        {
            context.Fail(); // 授权失败            
            if (currentHttpContext == null)
                return;
            currentHttpContext.SignoutToSwagger();
        }
    }
    public override Task<bool> PipelineAsync(AuthorizationHandlerContext context, DefaultHttpContext httpContext)
    {

        // 这里写您的授权判断逻辑，授权通过返回 true，否则返回 false
        return CheckAuthorizationAsync(httpContext);
    }

    /// <summary>
    /// 检查权限
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    private static async Task<bool> CheckAuthorizationAsync(DefaultHttpContext httpContext)
    {
        //超级管理员都能访问
        if (UserManager.SuperAdmin) return true;

        // 路由/按钮名称
        var routeName = httpContext.Request.Path.Value;
        //判断控制器是否有[RoleDataScope]特性，如果有则进行接口权限校验
        var controller = httpContext.GetControllerActionDescriptor();
        var controllerDataScope = controller.ControllerTypeInfo.GetCustomAttribute<RoleDataScopeAttribute>();
        if (controllerDataScope != null)
        {
            //方法是否忽略接口权限校验
            var ignoreRole = controller.MethodInfo.GetCustomAttribute<IgnoreRoleDataScopeAttribute>();
            if (ignoreRole == null) { //如果没有忽略权限校验则进行接口权限校验

                //获取用户接口权限
                var dataScopes = await App.GetService<IBaseService>().GetUserDataScope();

                if (dataScopes == null || dataScopes.Count == 0) return false;

                //根据角色数据范围信息匹配
                return dataScopes.Any(u => u.RouteUrl == routeName);
            }
            else//否则判断是否有菜单权限
            {
                return await CheckMenuRole(routeName);
            }
        }
        //否则判断是否有菜单权限
        else
        {
            var dataScopeRole = controller.MethodInfo.GetCustomAttribute<RoleDataScopeAttribute>();
            if (dataScopeRole != null)
            {
                //获取角色信息
                var dataScopes = await App.GetService<IBaseService>().GetUserDataScope();

                if (dataScopes == null || dataScopes.Count == 0) return false;

                //根据角色数据范围信息匹配
                return dataScopes.Any(u => u.RouteUrl == routeName);
            }
            else
            {
                return await CheckMenuRole(routeName);              
            }
        }
    }

    /// <summary>
    /// 验证用户token是否有效
    /// </summary>
    private static bool CheckToken(DefaultHttpContext context,int expire)
    {
        //当前上下文token
        var token = JWTEncryption.GetJwtBearerToken(context);
        //当前用户id
        var userId = App.User?.FindFirstValue(ClaimConst.UserId);
        //获取缓存实例
        var cache = App.GetService<IRedisCacheManager>();
        var cacheKey = CacheConst.UserToken + userId;
        var tokenList = cache.Get<List<TokenInfo>>(cacheKey);
        if (tokenList == null) return false;

        var tokenInfo= tokenList.Where(it=>it.Token==token).FirstOrDefault();
        if (tokenInfo == null) return false;

        var accessToken= context.Response.Headers["access-token"].ToString();
        if (!string.IsNullOrEmpty(accessToken))//如果有新的刷新token
        {
            tokenList.Remove(tokenInfo);//移除之前旧的token信息
            tokenInfo.Token = accessToken;//新的token
            tokenInfo.TokenExpireTime = DateTime.Now.AddMinutes(expire);//新的过期时间
            tokenList.Add(tokenInfo);//添加信息的token信息
            cache.Set(cacheKey, tokenList);
        }
        return true;
    }

    private static async Task<bool> CheckMenuRole(string routeName)
    {
        routeName = routeName.StartsWith("/api")
                ? routeName[5..].Replace("/", ":")
                : routeName[1..].Replace("/", ":");

        var userMenuList = await App.GetService<ISysUserService>().GetLoginUserMenu();
        var allMenuList= await App.GetService<IMenuService>().GetMenuList();
        var existUserMenu = userMenuList.Any(it => it.Path == routeName);
        var existAllMenu = allMenuList.TrueForAll(it => it.Path == routeName);
        //只有当在用户菜单中存在或者在所有菜单中不存在时才返回true
        return existUserMenu || !existAllMenu;
    }
}
